﻿namespace WLFramework.Security
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public class AlgorithmHash : IChecksum
    {
        protected System.Security.Cryptography.HashAlgorithm algorithm;

        protected byte[] hash;

        protected AlgorithmHash()
        {
            var aes = System.Security.Cryptography.Aes.Create();
        }

        protected AlgorithmHash(System.Security.Cryptography.HashAlgorithm algorithm)
        {
            Shield.AssertNotNull(ref algorithm, "algorithm");
            this.algorithm = algorithm;
        }

        public virtual long Value
        {
            get
            {
                return hash == null ? 0 : BitConverter.ToInt64(hash, 0);
            }
        }

        public virtual string HashCode
        {
            get
            {
                if (hash == null) return string.Empty;
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < this.hash.Length; i++)
                {
                    sb.Append(this.hash[i].ToString("x2"));
                }

                return sb.ToString();
            }
        }

        public virtual byte[] HashBytes
        {
            get 
            { 
                return this.hash; 
            }
        }

        public virtual void Reset()
        {
            this.hash = null;
        }

        public virtual void Update(int value)
        {
            this.hash = this.algorithm.ComputeHash(BitConverter.GetBytes(value));
        }

        public virtual void Update(byte[] buffer)
        {
            Shield.AssertNotNull(ref buffer, "buffer");
            this.hash = this.algorithm.ComputeHash(buffer);
        }

        public virtual void Update(byte[] buffer, int offset, int count)
        {
            Shield.AssertNotNull(ref buffer, "buffer");
            Shield.AssertIsNotTrue(count < 0, "count");
            Shield.AssertIsNotTrue((offset < 0 || offset + count > buffer.Length), "offset");

            this.hash = this.algorithm.ComputeHash(buffer, 0, count);
        }

        public virtual void Update(System.IO.Stream stream)
        {
            Shield.AssertNotNull(ref stream, "stream");
            Shield.AssertIsTrue(stream.CanRead, "stream.CanRead");

            this.hash = this.algorithm.ComputeHash(stream);
        }

        public virtual void Update(string input)
        {
            this.Update(input, Encoding.UTF8);
        }

        public virtual void Update(string input, Encoding encode)
        {
            Shield.AssertNotNull(ref input, "input");
            Shield.AssertNotNull(ref encode, "encode");

            this.hash = this.algorithm.ComputeHash(Encoding.UTF8.GetBytes(input));
        }
    }
}
